<script lang='ts' setup>
import { onMounted, onUnmounted, reactive } from 'vue'
import { Task, tasks } from '@/apis/toolbox/downloader/tasks.ts'
import StartTask from '@/components/toolbox/downloader/DownloaderStartTask.vue'
import { StartedTask } from '@/apis/toolbox/downloader/start_task.ts'
import { ProgressTask, query_progress } from '@/apis/toolbox/downloader/query_progress.ts'
import { byte_format } from '@/funcs/byte_format.ts'
import { cancel_task } from '@/apis/toolbox/downloader/cancel_task.ts'
import { retry_task } from '@/apis/toolbox/downloader/retry_task.ts'
import { remove_cache_file } from '@/apis/toolbox/downloader/remove_cache_file.ts'
import { generate_download_token } from '@/apis/basic/generate_download_token.ts'
import { date_format } from '@/funcs/date_format.ts'

const state = reactive<{ loading: boolean, refresh: boolean, tasks: Task[], show: boolean, id: NodeJS.Timeout | undefined }>({ loading: true, refresh: false, tasks: [], show: false, id: undefined })

onMounted(async () => {
  refresh().then(() => {
    state.loading = false
  })
})

onUnmounted(async () => {
  if (undefined !== state.id) {
    clearTimeout(state.id)
  }
})

async function refresh(): Promise<void> {
  const response = await tasks()
  state.refresh = false

  if (0 !== response.code) {
    window.$message.error(response.message)

    return
  }

  state.tasks = response.data.data
  if (undefined == state.id) {
    await progress()
  }
}

async function started(task: StartedTask): Promise<void> {
  state.tasks.unshift({ download_size: 0, end_time: 0, expired: 0, target: 1, file_size: task.file_size, filename: task.filename, id: task.id, message: '', canceling: false, removing: false, start_time: task.start_time, state: 1, local_file_removed: false, retrying: false, downloading: false })
  if (undefined == state.id) {
    await progress()
  }
}

async function progress(): Promise<void> {
  const ids: string[] = []
  state.tasks.forEach(task => {
    if (1 === task.state) {
      ids.push(task.id)
    }
  })

  if (0 === ids.length) {
    state.id = undefined

    return
  }

  const response = await query_progress(ids)
  if (0 !== response.code) {
    window.$message.error(response.message)
  } else {
    const m: { [key: string]: ProgressTask } = {}
    response.data.data.forEach(task => {
      m[task.id] = task
    })

    state.tasks.forEach(task => {
      if (!(task.id in m)) {
        return
      }

      const pk = m[task.id]
      task.state = pk.state
      task.message = pk.message
      task.end_time = pk.end_time
      task.download_size = pk.download_size
      task.expired = pk.expired
    })
  }

  state.id = setTimeout(progress, 1000)
}

async function cancel(task: Task): Promise<void> {
  task.canceling = true
  const response = await cancel_task(task.id)
  task.canceling = false

  if (0 !== response.code) {
    window.$message.error(response.message)

    return
  }

  window.$message.success('请求成功，任务已取消')
}

async function retry(task: Task): Promise<void> {
  task.canceling = true
  const response = await retry_task(task.id)
  task.canceling = false

  if (0 !== response.code) {
    window.$message.error(response.message)

    return
  }

  task.state = 1
  task.message = ''
  task.start_time = response.data.start_time
  task.download_size = 0

  window.$message.success('请求成功，开始重新下载文件')
  if (undefined === state.id) {
    await progress()
  }
}

async function remove(task: Task): Promise<void> {
  task.canceling = true
  const response = await remove_cache_file(task.id)
  task.canceling = false

  if (0 !== response.code) {
    window.$message.error(response.message)

    return
  }

  task.local_file_removed = true
  window.$message.success('服务端缓存文件已移除')
}

async function download(task: Task): Promise<void> {
  task.downloading = true
  const response = await generate_download_token()
  task.downloading = false

  if (0 !== response.code) {
    window.$message.error(response.message)

    return
  }

  window.$message.success('文件即将开始下载')
  window.open(`/api/toolbox/downloader/download?id=${task.id}&downloader-temporary-token=${response.data.token}`)
}
</script>

<template>
  <n-space vertical>
    <div>
      <n-space align='center' justify='space-between'>
        <n-button :loading='state.refresh' size='small' type='default' @click='state.refresh = true; refresh()'>刷新列表</n-button>
        <n-button size='small' type='primary' @click='state.show = true'>添加任务</n-button>
      </n-space>
    </div>
    <n-divider />
    <div>
      <template v-if='state.loading'>
        <n-space vertical>
          <n-skeleton height='25px' width='100%' />
          <n-skeleton height='25px' width='100%' />
          <n-skeleton height='25px' width='100%' />
        </n-space>
      </template>
      <template v-else>
        <template v-if='0 === state.tasks.length'>
          <n-space align='center' justify='center' style='height: 550px; font-size: 120%'>
            <n-text>当前没有已添加的下载任务</n-text>
          </n-space>
        </template>
        <n-space vertical>
          <template v-for='task in state.tasks' :key='task.id'>
            <div>
              <n-space :wrap-item='false' align='center' justify='space-between'>
                <div class='flex-max'>
                  <n-space :wrap-item='false' align='center'>
                    <n-ellipsis style='width: 300px;'>{{ task.filename }}</n-ellipsis>
                    <n-text style='width: 60px;'>{{ { 1: '下载中', 2: '失败', 3: '下载完成', 4: '已取消' }[task.state] }}</n-text>
                    <div class='flex-max'>
                      <n-progress :percentage='task.download_size / task.file_size * 100' :show-indicator='false' status='success' type='line' />
                    </div>
                    <div style='width: 150px;'>
                      <n-text>{{ byte_format(task.download_size) }}/{{ byte_format(task.file_size) }}</n-text>
                    </div>
                  </n-space>
                </div>
                <div style='width: 85px;'>{{ task.expired > 0 ? date_format(task.expired * 1000, 'yyyy-MM-dd') : '-' }}</div>
                <div style='width: 240px;'>
                  <n-space align='center' justify='end'>
                    <n-button v-if='1 === task.state' :loading='task.canceling' size='small' type='error' @click='cancel(task)'>取消</n-button>
                    <n-button v-if='2 === task.state || 4 === task.state' :loading='task.retrying' size='small' type='primary' @click='retry(task)'>重试</n-button>
                    <n-button v-if='3 === task.state' :disabled='task.local_file_removed && 1 !== task.target' size='small' type='primary' @click='download(task)'>下载到本地</n-button>
                    <n-button v-if='3 === task.state' :disabled='task.local_file_removed || 1 === task.target' :loading='task.removing' size='small' type='error' @click='remove(task)'>移除缓存文件</n-button>
                  </n-space>
                </div>
              </n-space>
            </div>
          </template>
        </n-space>
      </template>
    </div>
  </n-space>
  <StartTask v-model:show='state.show' @started='started' />
</template>

<style scoped>
.task-filename {
  width: 120px;
}
</style>
